﻿/*
using UnityEngine;
using UniFramework.Event;
using UniFramework.Singleton;
using YooAsset;

public class Boot : MonoBehaviour
{
	/// <summary>
	/// 资源系统运行模式
	/// </summary>
	public EPlayMode PlayMode = EPlayMode.EditorSimulateMode;

	void Awake()
	{
		Debug.Log($"资源系统运行模式：{PlayMode}");
		Application.targetFrameRate = 60;
		Application.runInBackground = true;
	}
	void Start()
	{
		// 初始化事件系统
		UniEvent.Initalize();

		// 初始化单例系统
		UniSingleton.Initialize();

		// 初始化资源系统
		YooAssets.Initialize();
		YooAssets.SetOperationSystemMaxTimeSlice(30);

		// 创建补丁管理器
		UniSingleton.CreateSingleton<PatchManager>();

		// 开始补丁更新流程
		PatchManager.Instance.Run(PlayMode);
	}

}
*/

using System;
using UniFramework.Event;
using UniFramework.Singleton;
using System.Collections;
using System.IO;
using UnityEngine;
using YooAsset;
using System.Linq;
using System.Reflection;
using HybridCLR;
using System.Collections.Generic;
using System.Threading.Tasks;

public class Boot : MonoBehaviour
{
	/// <summary>
	/// 资源系统运行模式
	/// </summary>
	public EPlayMode PlayMode = EPlayMode.EditorSimulateMode;

	void Awake()
	{
		Debug.Log($"资源系统运行模式：{PlayMode}");
		Application.targetFrameRate = 60;
		Application.runInBackground = true;
	}
	void Start()
	{
		// 初始化事件系统
		UniEvent.Initalize();

		// 初始化单例系统
		UniSingleton.Initialize();

		//// 初始化资源系统
		//YooAssets.Initialize();
		//YooAssets.SetOperationSystemMaxTimeSlice(30);

		//// 创建补丁管理器
		//UniSingleton.CreateSingleton<PatchManager>();

		//// 开始补丁更新流程
		//PatchManager.Instance.Run(PlayMode);

		// 更新资源包
		StartCoroutine(TestLoad());

	}

	#region 热更新脚本

	public static byte[] ReadBytesFromStreamingAssets(string file)
	{
		// Android平台不支持直接读取StreamingAssets下文件，请自行修改实现
		return File.ReadAllBytes($"{Application.streamingAssetsPath}/{file}");
	}

	private static Assembly _hotUpdateAss;
    
    void HotUptate()
	{
		// 补充元数据
		//LoadMetadataForAOTAssemblies();
		StartCoroutine(LoadMetadataForAOTAssemblies());

		// 加载热更Dll，通过反射运行程序
		/*
#if !UNITY_EDITOR
        _hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("HotUpdate.dll.bytes"));
#else
		_hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");
#endif
*/
		StartCoroutine(LoadHotDll());
		

		// 通过创建预制体运行程序
		Run_InstantiateComponentByAsset();

#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
        // 以下代码只为了方便自动化测试，与演示无关
        File.WriteAllText("run.log", "ok", System.Text.Encoding.UTF8);
        if (File.Exists("autoexit"))
        {
            Debug.Log("==== 本程序将于3秒后自动退出 ====");
            Task.Run(async () =>
            {
                await Task.Delay(3000);
                Application.Quit(0);
            });
        }
#endif
	}
	private IEnumerator LoadHotDll()
	{
		var package = YooAssets.TryGetPackage("DefaultPackage");
		var handle = package.LoadRawFileAsync("HotUpdate.dll");
		yield return handle;
		byte[] fileData = handle.GetRawFileData();

		_hotUpdateAss = Assembly.Load(fileData);

		// 通过反射运行程序
		Type entryType = _hotUpdateAss.GetType("Entry");
		entryType.GetMethod("Start").Invoke(null, null);

		entryType = _hotUpdateAss.GetType("Hello");
		entryType.GetMethod("Run").Invoke(null, null);
	}

	private void Run_InstantiateComponentByAsset()
	{
		// 通过实例化assetbundle中的资源，还原资源上的热更新脚本
		//AssetBundle ab = AssetBundle.LoadFromMemory(Boot.ReadBytesFromStreamingAssets("prefabs"));
		//GameObject cube = ab.LoadAsset<GameObject>("Cube");
		//GameObject.Instantiate(cube);

		StartCoroutine(InstantiatePrefab());
	}
	
	private IEnumerator InstantiatePrefab()
	{
		var package = YooAssets.TryGetPackage("DefaultPackage");
		var handle = package.LoadAssetAsync<GameObject>("Cube");
		yield return handle;
		var canvas = handle.InstantiateSync();
	}

	private IEnumerator LoadMetadataForAOTAssemblies()
	{
		var package = YooAssets.TryGetPackage("DefaultPackage");
		List<string> aotMetaAssemblyFiles = new List<string>()
		{
			"mscorlib.dll",
			"System.dll",
			"System.Core.dll",
			"Assembly-CSharp.dll"
		};
		/// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
		/// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
		/// 
		HomologousImageMode mode = HomologousImageMode.SuperSet;
		foreach (var aotDllName in aotMetaAssemblyFiles)
		{
			//byte[] dllBytes = ReadBytesFromStreamingAssets(aotDllName + ".bytes");
			//// 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
			//LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
			//Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");

			// YooAsset加载
			var handle = package.LoadRawFileAsync(aotDllName);
			yield return handle;
			byte[] fileData = handle.GetRawFileData();
			LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(fileData, mode);
			Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
		}
	}

    #endregion

    #region 热更新资源包
    private IEnumerator TestLoad()
	{
		yield return null;

		// 初始化资源系统
		YooAssets.Initialize();

		// 创建默认的资源包
		string packageName = "DefaultPackage";
		var package = YooAssets.TryGetPackage(packageName);
		if (package == null)
		{
			package = YooAssets.CreatePackage(packageName);
			// 设置该资源包为默认的资源包，可以使用YooAssets相关加载接口加载该资源包内容。
			YooAssets.SetDefaultPackage(package);
		}
		/*
		// 联机模式备用方案
        if (PlayMode == EPlayMode.HostPlayMode)
        {
			// 如果获取远端资源版本成功，说明当前网络连接通畅，可以走正常更新流程。否则单机运行
			var operation1 = package.UpdatePackageVersionAsync();
			yield return operation1;
			if (operation1.Status != EOperationStatus.Succeed)
			{
				PlayMode = EPlayMode.OfflinePlayMode;
			}
		}
		*/
		if (PlayMode == EPlayMode.EditorSimulateMode)
		{
			// 编辑器运行模式
			var createParameters = new EditorSimulateModeParameters();
			createParameters.SimulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(packageName);
			yield return package.InitializeAsync(createParameters);
		}
		else if (PlayMode == EPlayMode.HostPlayMode)
		{
			// 联机运行模式
			var createParameters = new HostPlayModeParameters();
			createParameters.DecryptionServices = new GameDecryptionServices();
			createParameters.QueryServices = new GameQueryServices();
			createParameters.DefaultHostServer = GetHostServerURL();
			createParameters.FallbackHostServer = GetHostServerURL();
			Debug.Log(GetHostServerURL());
			yield return package.InitializeAsync(createParameters);

		}
		else if (PlayMode == EPlayMode.OfflinePlayMode)
		{
			// 单机运行模式
			var createParameters = new OfflinePlayModeParameters();
			createParameters.DecryptionServices = new GameDecryptionServices();
			yield return package.InitializeAsync(createParameters);
		}
		/*
		if (package.InitializeStatus != EOperationStatus.Succeed)
		{
			Debug.LogWarning($"{initializationOperation.Error}");
			yield break;
		}
		*/

		// 获取资源版本
		var operation = package.UpdatePackageVersionAsync();
		yield return operation;

		if (operation.Status != EOperationStatus.Succeed)
		{
			//更新失败
			Debug.LogError(operation.Error);
			yield break;
		}
		//更新成功
		string packageVersion = operation.PackageVersion;
		Debug.Log($"Updated package Version : {packageVersion}");


		// 获取更新补丁清单
		var operation2 = package.UpdatePackageManifestAsync(packageVersion);
		yield return operation2;

		if (operation2.Status == EOperationStatus.Succeed)
		{
			//更新成功
			//注意：保存资源版本号作为下次默认启动的版本!
			operation2.SavePackageVersion();
		}
		else
		{
			//更新失败
			Debug.LogError(operation2.Error);
			yield break;
		}

		// 下载补丁
		yield return Download();

		// 清空未使用的缓存文件
		var operation3 = package.ClearUnusedCacheFilesAsync();
		yield return operation3;

		// 加载热更新脚本
		HotUptate();

		// 创建游戏管理器
		UniSingleton.CreateSingleton<GameManager>();

		// 开启游戏流程
		GameManager.Instance.Run();
	}

	private IEnumerator Download()
	{
		int downloadingMaxNum = 10;
		int failedTryAgain = 3;
		int timeout = 60;
		var package = YooAssets.GetPackage("DefaultPackage");
		var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain, timeout);

		//没有需要下载的资源
		if (downloader.TotalDownloadCount == 0)
		{
			yield break;
		}

		//需要下载的文件总数和总大小
		int totalDownloadCount = downloader.TotalDownloadCount;
		long totalDownloadBytes = downloader.TotalDownloadBytes;

		//注册回调方法
		downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;
		downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;
		downloader.OnDownloadOverCallback = OnDownloadOverFunction;
		downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;

		//开启下载
		downloader.BeginDownload();
		yield return downloader;

		//检测下载结果
		if (downloader.Status == EOperationStatus.Succeed)
		{
			//下载成功
			Debug.Log("下载完成！");
		}
		else
		{
			//下载失败
			Debug.Log("下载失败！！！");
		}
	}

	/// <summary>
	/// 开始下载
	/// </summary>
	/// <param name="fileName"></param>
	/// <param name="sizeBytes"></param>
	private void OnStartDownloadFileFunction(string fileName, long sizeBytes)
	{
		Debug.Log(string.Format("开始下载：文件名：{0}，文件大小：{1}", fileName, sizeBytes));
	}

	/// <summary>
	/// 下载完成
	/// </summary>
	/// <param name="isSucceed"></param>
	private void OnDownloadOverFunction(bool isSucceed)
	{
		Debug.Log("下载" + (isSucceed ? "成功" : "失败"));
	}

	/// <summary>
	/// 更新中
	/// </summary>
	/// <param name="totalDownloadCount"></param>
	/// <param name="currentDownloadCount"></param>
	/// <param name="totalDownloadBytes"></param>
	/// <param name="currentDownloadBytes"></param>
	private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes)
	{
		Debug.Log(string.Format("文件总数：{0}，已下载文件数：{1}，下载总大小：{2}，已下载文件大小：{3}",
			totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes));
	}

	/// <summary>
	/// 下载出错
	/// </summary>
	/// <param name="fileName"></param>
	/// <param name="error"></param>
	private void OnDownloadErrorFunction(string fileName, string error)
	{
		Debug.Log(string.Format("下载出错：文件名：{0}，错误信息：{1}", fileName, error));
	}

	/// <summary>
	/// 获取资源服务器地址
	/// </summary>
	private string GetHostServerURL()
	{
		//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
		string hostServerIP = "http://192.168.101.51";//"http://127.0.0.1";
		string gameVersion = "v2.0";

#if UNITY_EDITOR
		if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
			return $"{hostServerIP}/CDN/Android/{gameVersion}";
		else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
			return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
		else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
			return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
		else
			return $"{hostServerIP}/CDN/PC/{gameVersion}";
#else
		if (Application.platform == RuntimePlatform.Android)
			return $"{hostServerIP}/CDN/Android/{gameVersion}";
		else if (Application.platform == RuntimePlatform.IPhonePlayer)
			return $"{hostServerIP}/CDN/IPhone/{gameVersion}";
		else if (Application.platform == RuntimePlatform.WebGLPlayer)
			return $"{hostServerIP}/CDN/WebGL/{gameVersion}";
		else
			return $"{hostServerIP}/CDN/PC/{gameVersion}";
#endif
	}

	/// <summary>
	/// 内置文件查询服务类
	/// </summary>
	private class GameQueryServices : IQueryServices
	{
		public bool QueryStreamingAssets(string fileName)
		{
			string buildinFolderName = YooAssets.GetStreamingAssetBuildinFolderName();
			return StreamingAssetsHelper.FileExists($"{buildinFolderName}/{fileName}");
		}
	}

	/// <summary>
	/// 资源文件解密服务类
	/// </summary>
	private class GameDecryptionServices : IDecryptionServices
	{
		public ulong LoadFromFileOffset(DecryptFileInfo fileInfo)
		{
			return 32;
		}

		public byte[] LoadFromMemory(DecryptFileInfo fileInfo)
		{
			throw new NotImplementedException();
		}

		public Stream LoadFromStream(DecryptFileInfo fileInfo)
		{
			BundleStream bundleStream = new BundleStream(fileInfo.FilePath, FileMode.Open);
			return bundleStream;
		}

		public uint GetManagedReadBufferSize()
		{
			return 1024;
		}
	}
	#endregion
}